// Have include guard.
#ifndef FPK_SUBSCRIBER_HPP
#define FPK_SUBSCRIBER_HPP

// Include files.
#include <set>
#include <boost/smart_ptr/shared_ptr.hpp>
#include "Internal/NullDeleter.hpp"

struct Unspecified;

template<typename Result>
class Operation;

/**
 *  The Subscriber class is the base class for objects that subscribe to SystematicRobot's events, directly or indirectly.
 *  
 *  This class is untested.
 */
class Subscriber
{
	// Befriend our robot class so it can call our event handlers.
	friend class SystematicRobot;
	
	friend class Service;
	friend class RobotSubsystem;
	
	template<typename Result>
	friend class Operation;
	
	public:
		Subscriber();
		virtual ~Subscriber();
		
	protected:
		/**
		 *  Add required control lock on an object (such as a RobotSubsystem).
		 *  
		 *  The lock is only acquired when the subscriber/service/operation is running.
		 *  This prevents two unrelated subscribers from using the same object at the same time. 
		 *  For more information on using locking, see our Thread Safety Manuel that does not exist yet. :P
		 *  
		 *  @param obj A reference to the object to be locked.
		 *  
		 *  @returns The obj parameter.
		 */
		template<typename T>
		T &RequireControl(T &obj)
		{
			m_controlLocksRequired.insert(&obj);
			return obj;
		}
		
		/**
		 *  Launch an Operation within this Subscriber.
		 *  
		 *  @param operation A shared pointer to Operation to be launched.
		 *  
		 *  @returns A shared pointer to the launched Operation (same as "operation").
		 */
		template<typename T>
		boost::shared_ptr<T> Launch(boost::shared_ptr<T> operation)
		{
			LaunchInternal(operation);
			return operation;
		}
		
		/**
		 *  Launch an Operation within this Subscriber.
		 *  
		 *  @param operation A pointer to Operation to be launched.
		 *  @param canDeleteOperation If the Subscriber can delete the Operation so long as it functions correctly and no shared pointer is kept. 
		 *  
		 *  @returns A shared pointer to the launched operation.
		 */
		template<typename T>
		boost::shared_ptr<T> Launch(T *operation, bool canDeleteOperation = false)
		{
			boost::shared_ptr<T> operationShared;
			
			if (canDeleteOperation)
				operationShared = boost::shared_ptr<T>(operation);
			else
				operationShared = boost::shared_ptr<T>(operation, NullDeleter());
			
			LaunchInternal(operationShared);
			
			return operationShared;
		}
		
		void CancelAllOperations();
		virtual void HandlePeriodic();
	private:
		SystematicRobot *m_robot;
		Subscriber *m_parent;
		bool m_isSubscribed;
		std::set<Operation<Unspecified>*> m_operations;
		std::set<boost::shared_ptr<Subscriber> > m_children;
		std::set<void*> m_controlLocksRequired;
		
		// Make copy constructor and copy assignment operator private.
		Subscriber(const Subscriber &other) {}
		void operator=(const Subscriber &other) {}
		
		SystematicRobot *GetRobot();
		void SetRobot(SystematicRobot *robot);
		bool GetIsSubscribed() const;
		void SetIsSubscribed(bool isSubscribed);
		void AddChild(boost::shared_ptr<Subscriber> subscriber);
		void AddChild(Subscriber &subscriber);
		void RemoveChild(boost::shared_ptr<Subscriber> subscriber);
		void RemoveOperation(Operation<Unspecified> *operation);
		void RunPeriodic();
		void AquireLocks();
		void ReleaseLocks();
		void LaunchInternal(boost::shared_ptr<Operation<Unspecified> > operation);
};

#endif // #ifndef FPK_SUBSCRIBER_HPP
